package main

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"regexp"
	"strings"
	"sync"
)

var count = 0
var lock sync.Mutex
var basePath string
var ignoreFolders []string
var ignoreFiles []string

func init() {
	// 读取过滤文件
	var ignoreErr error
	ignoreFolders, ignoreFiles, ignoreErr = ScanIgnore()
	if ignoreErr != nil {
		fmt.Print("\n" + ignoreErr.Error() + "\n")
	}
}

func main() {
	basePath = "/var/Project/code-statistics/example"
	count := ScanDir(basePath)
	fmt.Print(count)
}

// ScanDir 扫描代码目录
func ScanDir(dir string) int {
	fd, openError := ioutil.ReadDir(dir)
	if openError != nil {
		fmt.Print("Error=", openError.Error())
	}

	for _, item := range fd {
		if !item.IsDir() {
			currentFile := dir + "\\" + item.Name()
			if !inspectFileInIgnore(currentFile) {
				f, _ := os.Open(dir + "\\" + item.Name())
				l, _ := LineCounter(bufio.NewReader(f))
				lock.Lock()
				count += l
				lock.Unlock()
			}
		} else {
			currentPath := dir + "\\" + item.Name()
			if !inspectFolderInIgnore(currentPath) {
				ScanDir(currentPath)
			}
		}
	}

	return count
}

// 检查文件夹是否在过滤文件内
func inspectFolderInIgnore(path string) bool {
	if len(ignoreFolders) == 0 {
		return false
	}
	for _, v := range ignoreFolders {
		if basePath+"\\"+v == path {
			return true
		}
	}
	return false
}

// 检查文件是否在过滤文件内
func inspectFileInIgnore(file string) bool {
	if len(ignoreFiles) == 0 {
		return false
	}
	for _, v := range ignoreFiles {
		reg := regexp.MustCompile(v)
		if reg.MatchString(file) {
			return true
		}
	}
	return false
}

func LineCounter(r io.Reader) (int, error) {
	buf := make([]byte, 32*1024)
	count := 0
	linesep := []byte{'\n'}
	for {
		c, err := r.Read(buf)
		count += bytes.Count(buf[:c], linesep)
		switch {
		case err == io.EOF:
			return count, nil
		case err != nil:
			return count, err
		}
	}
}

// ScanIgnore 读取过滤文件
func ScanIgnore() ([]string, []string, error) {
	fd, openErr := os.Open(".ignore")
	if openErr != nil {
		return []string{}, []string{}, errors.New("open ignore file failed")
	}

	defer func(fd *os.File) {
		err := fd.Close()
		if err != nil {

		}
	}(fd)

	reader := bufio.NewReader(fd)

	var folders []string
	var files []string
	// 按行处理txt
	for {
		line, _, err := reader.ReadLine()
		if err == io.EOF {
			break
		}
		// folder
		re := []int32(string(line))
		if string(re[len(line)-1:len(line)]) == "/" {
			folders = append(folders, strings.Replace(string(re[0:len(line)-1]), "/", "\\", -1))
		} else {
			// file
			file := strings.Replace(string(line), "/", "\\\\", -1)
			file = strings.Replace(file, "*", "\\\\*", -1)
			files = append(files, file)
		}
	}

	return folders, files, nil
}
